#include <QUrl>
#include <QDebug>
#include <QApplication>

#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>

#include <QSettings>
#include <QFileInfo>

#include <QReadLocker>
#include <QWriteLocker>
#include <QtConcurrent>


#include "filemanager.h"

static const char* recvFilePath = "recvFilePath";

// 配置文件相关
struct PrivateData
{
private:
    static QString fileName;

public:
    PrivateData() : settings(fileName, QSettings::IniFormat) {}
    QSettings settings;
    QString   recvPath;
};

QString PrivateData::fileName = QCoreApplication::applicationDirPath() + "/config/FileManager.ini";


void registerType()
{
    qRegisterMetaType<QSharedPointer<QFile>>("QSharedPointer<<QFile>");
}


FileManager::FileManager() :
    m_config(new PrivateData)
{
    registerType();
    m_config->recvPath = m_config->settings.value(recvFilePath, "RecvFile").toString();
    /* 文件接收存放目录 */
    QDir dir;
    if (!dir.exists(m_config->recvPath))
        dir.mkpath(m_config->recvPath);
}

FileManager::~FileManager()
{
    for (auto it = m_files.begin(); it != m_files.end(); ++it)
    {
        it.value()->close();    // close file
    }
    delete m_config;
}

FileManager* FileManager::instance()
{
    return Sington<FileManager>::getInstance();
}

QSharedPointer<QFile> FileManager::open(const QUrl &url, QIODevice::OpenModeFlag option)
{
    if (!url.isLocalFile()) {
        qWarning() << url.url() << " is not a local file.";
        return nullptr;
    }

    QString fileName = url.toString(QUrl::PreferLocalFile);
    return open(fileName, option);
}

QSharedPointer<QFile> FileManager::open(const QString &name, QIODevice::OpenModeFlag option)
{
    auto ptr = getFile(name);
    if (!ptr.isNull())
        return ptr;

    QSharedPointer<QFile> file(new QFile(name));
    if (!file->open(option)) {
        qCritical() << "Open" << file->fileName() << "failed!" << file->errorString();
        return nullptr;
    }
    qDebug() << "Open" << file->fileName() << "success!";
    addFile(file);
    return file;
}

QSharedPointer<QFile> FileManager::openForRead(const QUrl &url)
{
    return FileManager::open(url, QIODevice::ReadOnly);
}

QSharedPointer<QFile> FileManager::openForWrite(const QUrl &url)
{
    return FileManager::open(url, QIODevice::WriteOnly);
}

QSharedPointer<QFile> FileManager::openForRead(const QString &name)
{
    return FileManager::open(name, QIODevice::ReadOnly);
}

QSharedPointer<QFile> FileManager::openForWrite(const QString &name)
{
    return FileManager::open(name, QIODevice::WriteOnly);
}

QSharedPointer<QFile> FileManager::getFile(const QString &name)
{
    QReadLocker lock(&m_rwlock);
    auto it = m_files.find(name);
    if (it == m_files.end())
        return nullptr;
    return it.value();
}

void FileManager::addFile(QSharedPointer<QFile> file)
{
    if (!runInLoop()) {
        QMetaObject::invokeMethod(this, "addFile",
                                  Q_ARG(QSharedPointer<QFile>, file));
        return;
    }

    connect(file.get(), &QFile::aboutToClose, this, &FileManager::handleClose);

    QWriteLocker lock(&m_rwlock);
    auto it = m_files.find(file->fileName());
    if (it != m_files.end()) {
        qCritical() << "file" << file->fileName() << "already exits.";
        return;
    }
    m_files[file->fileName()] = file;
}

bool FileManager::setFileRecvPath(QString &path)
{
    if (m_config->recvPath != path) {
        m_config->recvPath = path;
        /* 文件接收存放目录 */
        QDir dir;
        if (!dir.exists(path))
            dir.mkpath(path);
        m_config->settings.setValue(recvFilePath, path);
        return true;
    }
    return false;
}

QString FileManager::fileRecvPath() const
{
    return m_config->recvPath;
}

void FileManager::handleClose()
{
    QFile* file = reinterpret_cast<QFile*>(sender());
    if (QFileDevice::NoError == file->error()) {
        qDebug() << "Close file" << file->fileName();
    } else {
        qDebug() << "File error" << file->errorString();
    }
    QString name = file->fileName();
    QWriteLocker lock(&m_rwlock);
    auto it = m_files.find(name);
    if (it != m_files.end()) {
        m_files.erase(it);
    }
}

bool FileManager::runInLoop() const
{
    return QThread::currentThread() == thread();
}


